/*
 *    Copyright (c) 2018-2025, boss All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the boss developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: boss
 */
package com.hopes.bizz.line.pipe.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hopes.bizz.common.core.util.CoordinateSystemUtils;
import com.hopes.bizz.common.core.validate.ValidatorUtils;
import com.hopes.bizz.line.constant.enums.ParkLineSysDictEnum;
import com.hopes.bizz.line.pipe.dto.CoordinateDTO;
import com.hopes.bizz.line.pipe.dto.LinePipeExcelDTO;
import com.hopes.bizz.line.pipe.entity.LinePipeEntity;
import com.hopes.bizz.line.pipe.entity.LinePipelineEntity;
import com.hopes.bizz.line.pipe.entity.LineValveEntity;
import com.hopes.bizz.line.pipe.mapper.LinePipeMapper;
import com.hopes.bizz.line.pipe.mapper.LinePipelineMapper;
import com.hopes.bizz.line.pipe.service.LinePipeService;
import com.hopes.bizz.line.pipe.service.LineValveService;
import com.hopes.bizz.line.pipe.vo.LinePipeExcelVO;
import com.hopes.bizz.line.pipe.vo.LinePipeVO;
import com.hopes.bizz.line.pipe.vo.PipeLineStatisticVO;
import com.hopes.boss.admin.api.entity.SysDictItem;
import com.hopes.boss.admin.api.resolver.DictResolver;
import com.hopes.boss.common.core.exception.BizException;
import com.hopes.boss.common.entity.ValidateImportBO;
import com.hopes.boss.common.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 管段
 *
 * @author pig
 * @date 2024-02-21 21:46:39
 */
@Service
@RequiredArgsConstructor
public class LinePipeServiceImpl extends ServiceImpl<LinePipeMapper, LinePipeEntity> implements LinePipeService {

    private final LineValveService lineValveService;
    private final LinePipelineMapper linePipelineMapper;
    private final static DecimalFormat DF = new DecimalFormat("#.00");

    @Override
    public IPage<LinePipeVO> pageVo(Page page, LambdaQueryWrapper<LinePipeEntity> wrapper) {
        final Page<LinePipeEntity> selectPage = getBaseMapper().selectPage(page, wrapper);
        final List<Long> pipeList = selectPage.getRecords().stream().map(LinePipeEntity::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(pipeList)) {
            return new Page<>(selectPage.getCurrent(), selectPage.getSize(), selectPage.getTotal(), selectPage.searchCount());
        }
        final Map<Long, List<LineValveEntity>> listMap = lineValveService.list(
                Wrappers.<LineValveEntity>lambdaQuery().in(LineValveEntity::getPipeId, pipeList)
        ).stream().filter(e -> ObjUtil.isNotNull(e.getPipeId())).collect(Collectors.groupingBy(LineValveEntity::getPipeId));

        final List<LinePipeVO> collect = selectPage.getRecords().stream().map(e -> {
            final LinePipeVO linePipeVO = new LinePipeVO();
            BeanUtils.copyProperties(e, linePipeVO);
            linePipeVO.setLineValveList(listMap.get(e.getId()));
            return linePipeVO;
        }).collect(Collectors.toList());

        final Page<LinePipeVO> voPage = new Page<>(selectPage.getCurrent(), selectPage.getSize(), selectPage.getTotal(), selectPage.searchCount());
        voPage.setRecords(collect);
        return voPage;
    }

    @Override
    public PipeLineStatisticVO pipeStatistic(List<LinePipeEntity> pipeList) {
        final PipeLineStatisticVO result = new PipeLineStatisticVO();
        if (CollUtil.isEmpty(pipeList)) {
            return result;
        }
        result.setPipeLength(calcPipeLength(pipeList));
        result.setPipeRadius(calcPipeRadius(pipeList));

        final List<Long> pipeLineIdList = pipeList.stream().map(LinePipeEntity::getPipelineId).collect(Collectors.toList());
        final List<LinePipelineEntity> linePipelineList = linePipelineMapper.selectList(Wrappers.<LinePipelineEntity>lambdaQuery().in(LinePipelineEntity::getId, pipeLineIdList));
        if (CollUtil.isEmpty(linePipelineList)) {
            return result;
        }

        result.setLineMaterial(calcLineMaterial(linePipelineList));
        result.setLineEmbedType(calcLineEmbedType(linePipelineList));
        return result;
    }

    @Override
    public List<LinePipeEntity> queryByArea(List<CoordinateDTO> coordinateList) {
        if (CollUtil.isEmpty(coordinateList)) {
            return new ArrayList<>();
        }
        final JSONArray array = JSONArray.parseArray(JSON.toJSONString(coordinateList));
        // 1、查询所有管段
        final List<LinePipeEntity> pipeList = this.list();
        final Map<Long, LinePipeEntity> linePipeMap = pipeList.stream().filter(r -> StrUtil.isNotBlank(r.getPosition())).collect(Collectors.toMap(LinePipeEntity::getId, e -> e));
        for (Map.Entry<Long, LinePipeEntity> entry : linePipeMap.entrySet()) {
            final JSONArray position = JSONArray.parseArray(entry.getValue().getPosition());
            final boolean lineInPolygon = CoordinateSystemUtils.isLineInPolygon(position, array);
            if (!lineInPolygon) {
                pipeList.remove(entry.getValue());
            }
        }
        return pipeList;
    }

    private List<Map<String, Object>> calcLineEmbedType(List<LinePipelineEntity> linePipelineList) {
        final List<SysDictItem> sysDictItemList = DictResolver.getItemsByType(ParkLineSysDictEnum.line_embed_type);
        if (CollUtil.isEmpty(sysDictItemList)) {
            return new ArrayList<>();
        }
        final int size = linePipelineList.size();
        final Map<String, Long> longMap = linePipelineList.stream().filter(e ->
                ObjUtil.isNotEmpty(e.getEmbedType())).collect(Collectors.groupingBy(LinePipelineEntity::getEmbedType, Collectors.counting()));
        return sysDictItemList.stream().map(e -> new HashMap<String, Object>() {
            private static final long serialVersionUID = -8381915683163991252L;
            {
                put("name", e.getName());
                put("value", Double.parseDouble(DF.format(longMap.getOrDefault(e.getCode(), 0L))) / size * 100);
            }
        }).collect(Collectors.toList());
    }

    private List<Map<String, Object>> calcLineMaterial(List<LinePipelineEntity> linePipelineList) {
        final List<SysDictItem> sysDictItemList = DictResolver.getItemsByType(ParkLineSysDictEnum.line_material_type);
        if (CollUtil.isEmpty(sysDictItemList)) {
            return new ArrayList<>();
        }
        final int size = linePipelineList.size();
        final Map<String, Long> longMap = linePipelineList.stream().filter(e ->
                ObjUtil.isNotEmpty(e.getLineMaterial())).collect(Collectors.groupingBy(LinePipelineEntity::getLineMaterial, Collectors.counting()));
        return sysDictItemList.stream().map(e -> new HashMap<String, Object>() {
            private static final long serialVersionUID = -8381915683163991252L;
            {
                put("name", e.getName());
                put("value", Double.parseDouble(DF.format(longMap.getOrDefault(e.getCode(), 0L))) / size * 100);
            }
        }).collect(Collectors.toList());
    }

    private List<Map<String, Object>> calcPipeRadius(List<LinePipeEntity> pipeList) {
        final int size = pipeList.size();
        final Map<BigDecimal, Long> longMap = pipeList.stream().filter(e -> ObjUtil.isNotNull(e.getPipeRadius())).collect(Collectors.groupingBy(LinePipeEntity::getPipeRadius, Collectors.counting()));
        return longMap.entrySet().stream().map(e -> new HashMap<String, Object>() {
            private static final long serialVersionUID = -4367148735891162392L;
            {
                put("name", e.getKey());
                put("value", Double.valueOf(DF.format(e.getValue().doubleValue() / size * 100)));
            }
        }).collect(Collectors.toList());
    }

    private List<Map<String, Object>> calcPipeLength(List<LinePipeEntity> pipeList) {
        return pipeList.stream().filter(e -> ObjUtil.isNotNull(e.getPipeLength())).map(e -> new HashMap<String, Object>() {
            private static final long serialVersionUID = -4367148735891162392L;
            {
                put("name", e.getPipeCode());
                put("value", e.getPipeLength());
            }
        }).collect(Collectors.toList());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeBatchByIds(Collection<?> list) {
        final long count = lineValveService.count(Wrappers.<LineValveEntity>lambdaQuery().in(LineValveEntity::getPipeId, list));
        if (count > 0) {
            throw new BizException("存在关联的阀门信息，不可删除！");
        }
        return removeBatchByIds(list, DEFAULT_BATCH_SIZE);
    }

    @Override
    public ValidateImportBO<LinePipeExcelDTO> validateImport(List<LinePipeExcelDTO> excelDTOList) {
        ValidateImportBO<LinePipeExcelDTO> bo = new ValidateImportBO<>();
        List<ErrorMessage> errorMessageList = new ArrayList<>();
        List<LinePipeExcelDTO> saveList = new ArrayList<>();
        int successNum = 0;
        int failedNum = 0;

        for (LinePipeExcelDTO linePipeExcelDTO : excelDTOList) {
            final String validateRes = ValidatorUtils.importValidateEntity(linePipeExcelDTO);
            if (StrUtil.isEmpty(validateRes)) {
                successNum++;
                saveList.add(linePipeExcelDTO);
            } else {
                failedNum++;
                errorMessageList.add(new ErrorMessage(linePipeExcelDTO.getLineNum(), new HashSet<>(){
                    private static final long serialVersionUID = 1544007667186208650L;
                    {
                        add(validateRes);
                    }
                }));
            }
        }

        bo.setErrorMessages(errorMessageList);
        bo.setSaveList(saveList);
        bo.setSuccessNum(successNum);
        bo.setFailedNum(failedNum);
        return bo;
    }

    @Override
    public LinePipeEntity importDtoToEntity(LinePipeExcelDTO excelDto) {
        LinePipeEntity entity= new LinePipeEntity();
        BeanUtils.copyProperties(excelDto, entity);
        return entity;
    }

    @Override
    public LinePipeExcelVO exportEntityToVo(LinePipeEntity entity) {
        LinePipeExcelVO excelVO = new LinePipeExcelVO();
        BeanUtils.copyProperties(entity, excelVO);
        excelVO.setPositionStr(StrUtil.isBlank(entity.getPosition()) ? "已定位" : "未定位");
        return excelVO;
    }


}
